/*CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
 C      semaphore.c
 C      Application Name: tcss422-project2
 C      Programmers Names: Evan Phillips, Lucas Cowell, Tony Butterfield
 C      Date created:   May 28, 2011
 C      Date Completed:
 C      Brief Description: Simulate a semaphore by managing owner and blocked queue.
 C
 CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC*/
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include "definitions.h"
#include "process.h"
#include "process_queue.h"
#include "semaphore.h"


/*=================================================================================================
 =  Comment block for function: semaphore_init()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      Semaphore semaphore_init(CPU)
 =      Return value ranges: pointer to Semaphore
 =      Parameter value ranges: n/a
 =      Error conditions: n/a
 =  Initializes Semaphore simulation.
 =
 ==================================================================================================*/
Semaphore semaphore_init()
{
    Semaphore s = (Semaphore) malloc(sizeof(SemaphoreStr));
    s->blocked_q = p_queue_init();
    s->owner = NULL; //set it to null for now, no one owns this
    return s;
}

/*=================================================================================================
 =  Comment block for function: semaphore_free()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      int semaphore_free(IODevice)
 =      Return value ranges: 0 on success, else error code
 =      Parameter value ranges: pointer to Semaphore
 =      Error conditions: NULL_POINTER_ERROR
 =  Deallocates a Semaphore.
 =
 ==================================================================================================*/
int semaphore_free(Semaphore s)
{
    if (s == NULL) return NULL_POINTER_ERROR;
    int error = p_queue_free(s->blocked_q);
    free(s);
    return error;
}

/*=================================================================================================
 =  Comment block for function: semaphore_down()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      int semaphore_down(Semaphore, Process)
 =      Return value ranges: 0 on success, else error code
 =      Parameter value ranges: pointer to Semaphore, pointer to Process
 =      Error conditions: NULL_POINTER_ERROR
 =  Requests ownership of semaphore.  If successful, it sets Owns and clears Waiting,
 =  else it clears Owns and sets Waiting.
 =
 ==================================================================================================*/
int semaphore_down(Semaphore s, Process p)
{
    if (s == NULL || p == NULL) return NULL_POINTER_ERROR;
    int error = 0;

    if (s->owner == NULL || p_getOwns(p, &error) == s)  // if semaphore up (no owner) or
    {                                                   // process already owns
        printf("(%s[%d] acquires semaphore)", p->pcb->name, p_getPID(p, &error));

    	s->owner = p;                               // set owner
    	error = p_setOwns(p, s);                    // set process status to show it got
    	error = p_setWaiting(p, NULL);              // ownership


    }
    else {                                          //else
    	//error = p_queue_enqueue(s->blocked_q, p);
    	error = p_setWaiting(p, s->blocked_q);      // set process status to show failed to get
    	error = p_setOwns(p, NULL);                 // ownership
    }
    return error;
}

/*=================================================================================================
 =  Comment block for function: semaphore_up()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      int semaphore_up(Semaphore, Process)
 =      Return value ranges: 0 on success, else error code
 =      Parameter value ranges: pointer to Semaphore, pointer to Process
 =      Error conditions: NULL_POINTER_ERROR
 =  Attempts to remove a process' control of the semaphore.
 =
 ==================================================================================================*/
int semaphore_up(Semaphore s, Process p)
{
    if (s == NULL || p == NULL) return NULL_POINTER_ERROR;
    int error = 0;

    if (s->owner == p) {                //can't touch this if you dont own it
    	s->owner = NULL;                // release ownership
    	error = p_setOwns(p, NULL);     // update status of the process that used to own it

    	if (p_queue_isEmpty(s->blocked_q, &error) == 0) {
        	//why write code twice, sem_down does exactly what we need
    		semaphore_down(s, p_queue_peek(s->blocked_q, &error));
    	}
    }
    else {
    	error = INVALID_ARGUMENT_ERROR; // if the process trying to free this doesn't own it.
    }
    return error;
}
